/* The custom memory chart widget */

#include <map>

#include <glibmm/i18n.h>
#include <glibmm/refptr.h>

#include <gdkmm/color.h>
#include <gdkmm/drawable.h>
#include <gdkmm/pixbuf.h>

#include <cairomm/context.h>
#include <cairomm/refptr.h>

#include <gtkmm/enums.h>
#include <gtkmm/layout.h>
#include <gtkmm/liststore.h>
#include <gtkmm/treemodel.h>


#include "chart_box.hpp"
#include "object_map.hpp"

#include "memory_chart.hpp"

namespace memchart
{
	MemoryChart::MemoryChart ()
		: layout_list_model(Gtk::ListStore::create(layout_list_columns)),
		need_update(false)
	{
		modify_bg(Gtk::STATE_NORMAL, Gdk::Color("white"));

		/* This sucks */
		std::map<LayoutAlgorithm, Glib::ustring> layouts;
		layouts[LAYOUT_TREE] = _("Tree");
		layouts[LAYOUT_LIST] = _("List");
		layouts[LAYOUT_DAG] = _("Directed acyclic graph");
		layouts[LAYOUT_GENERIC] = _("Generic graph");		
		for (std::map<LayoutAlgorithm, Glib::ustring>::const_iterator it =
			layouts.begin(); it != layouts.end(); ++it)
		{
			Gtk::TreeModel::Row row = *layout_list_model->append();
			row[layout_list_columns.id] = it->first;
			row[layout_list_columns.name] = it->second;
		}
	}

	Glib::RefPtr<Gdk::Pixbuf> MemoryChart::get_pixbuf ()
	{
		return Gdk::Pixbuf::create(
			static_cast<Glib::RefPtr<Gdk::Drawable> >(get_snapshot()),
			0, 0, get_width(), get_height());
	}

	void MemoryChart::update (const object_map & objects,
		LayoutAlgorithm advice)
	{
		update_boxes(objects);
		analyze_graph();

		if (is_list)
			layout_algorithm = LAYOUT_LIST;
		else if (is_tree)
			layout_algorithm = LAYOUT_TREE;
		else if (is_dag)
			layout_algorithm = LAYOUT_DAG;
		else
			layout_algorithm = LAYOUT_GENERIC;
		switch (advice)
		{
			case LAYOUT_LIST:
				if (is_list)
					layout_algorithm = LAYOUT_LIST;
				break;
			case LAYOUT_TREE:
				if (is_tree)
					layout_algorithm = LAYOUT_TREE;
				break;
			case LAYOUT_DAG:
				if (is_dag)
					layout_algorithm = LAYOUT_DAG;
				break;
			case LAYOUT_GENERIC:
				layout_algorithm = LAYOUT_GENERIC;
				break;
			default:
				break;
		}

		need_update = true;

		show_all_children();
		get_bin_window()->invalidate(false);
	}

	bool MemoryChart::on_expose_event (GdkEventExpose *event)
	{
		if (need_update)
		{
			/*
			 * This really should be in update(), but I can't find a way
			 * to get widget sizes before the expose event
			 */

			switch (layout_algorithm)
			{
				case LAYOUT_LIST:
					layout_list();
					break;
				case LAYOUT_TREE:
					layout_tree();
					break;
				case LAYOUT_DAG:
					layout_dag();
					break;
				default:
					layout_generic();
			}

			need_update = false;
			get_bin_window()->invalidate(false); /* Hmm */

			return true;
		}
		else
		{
			Cairo::RefPtr<Cairo::Context> cr =
				get_bin_window()->create_cairo_context();

			cr->rectangle(event->area.x, event->area.y,
				event->area.width, event->area.height);
			cr->clip();

			draw_arrows(cr);

			return Gtk::Layout::on_expose_event(event);
		}
	}
}
